#include "can.hpp"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <linux/ioctl.h>
#include <unistd.h>
#include <iostream>

enum CanFlag
{
    STD = 0,
    EFF = 0x80000000U,
    RTR = 0x40000000U,
    ERR = 0x20000000U
};

bool Can::Open(std::string dev)
{
    soc = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (soc < 0)
        return false;
    ifreq ifr;
    strcpy(ifr.ifr_name, dev.c_str());
    ioctl(soc, SIOCGIFINDEX, &ifr);
    sockaddr_can addr{.can_family = AF_CAN, .can_ifindex = ifr.ifr_ifindex};
    int ret = bind(soc, (const sockaddr *)&addr, sizeof(addr));
    if (ret < 0)
    {
        close(soc);
        return false;
    }
    EnableLoopBack(false);
    return true;
}

bool Can::SendFrame(const can_frame &frame)
{
    assert(frame.can_dlc <= 8);
    auto sendSize = write(soc, &frame, sizeof(frame));
    if (sendSize != sizeof(frame))
    {
        std::cout << errno << std::endl;
        return false;
    }
    return true;
}

const can_frame &Can::GetFrame()
{
    int readSize = read(soc, &recFrame, sizeof(recFrame));
    if (readSize < 0)
        throw "Read Fail";
    else if (readSize == 0)
        throw "Can socket disconnect";
    return recFrame;
}

bool Can::SetFilter(uint32_t id, uint32_t mask)
{
    can_filter filter{.can_id = id, .can_mask = mask};
    return 0 == setsockopt(soc, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter));
}

void Can::EnableLoopBack(bool enable)
{
    int loopback = enable ? 1 : 0;
    if (setsockopt(soc, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)) == -1)
    {
        throw "set socket loopback fail";
    }
}

Can::~Can()
{
    if (soc == 0)
        return;
    close(soc);
    soc = 0;
}